package update

import (
	"fmt"
	"gitee.com/zy_blue7/go-gorm-learn/insert"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

func Update(db *gorm.DB) {

	user := insert.User{
		Name: "zhangsan",
	}
	// SELECT * FROM `users` WHERE `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT 1
	db.Debug().First(&user)
	user.Age = 9999
	user.Name = "张三丰"
	// todo Save 会保存所有的字段，即使字段是零值
	//  UPDATE `users` SET `created_at`='2022-09-20 12:45:19.093',`updated_at`='2022-09-20 15:25:04.243',`deleted_at`=NULL,`name`='张三丰',`age`=9999,`birthday`='2022-09-20 12:45:19.052' WHERE `id` = 1
	db.Debug().Save(&user)
	// save 方法会默认添加 id，和 deleted_at=null 条件
	// UPDATE `users` SET `created_at`='2022-09-20 12:45:19.093',`updated_at`='2022-09-20 15:27:38.206',`deleted_at`=NULL,`name`='张三丰',`age`=9999,`birthday`='2022-09-20 12:45:19.052' WHERE name= '张三丰'  AND `users`.`deleted_at` IS NULL AND `id` = 1
	db.Debug().Where("name= ? ", "张三丰").Save(&user)

}

// OneColumnUpdate 更新单列
func OneColumnUpdate(db *gorm.DB) {

	// 当使用 Update 更新单个列时，你需要指定条件，否则会返回 ErrMissingWhereClause 错误，
	//查看 Block Global Updates 获取详情。当使用了 Model 方法，且该对象主键有值，该值会被用于构建条件，例如：

	user := insert.User{}
	db.Debug().First(&user)

	fmt.Println(user)

	// 条件更新
	// UPDATE `users` SET `name`='hello',`updated_at`='2022-09-20 15:40:55.509' WHERE age = 55 AND `users`.`deleted_at` IS NULL
	db.Debug().Model(&insert.User{}).Where("age = ?", 55).Update("name", "hello")

	// User 的 ID 是 `1`
	// UPDATE `users` SET `name`='hello',`updated_at`='2022-09-20 15:42:42.548' WHERE `id` = 1
	db.Debug().Model(&user).Update("name", "hello")

	// UPDATE `users` SET `name`='hello',`updated_at`='2022-09-20 15:44:03.359' WHERE age = 9999 AND `users`.`deleted_at` IS NULL AND `id` = 1
	db.Debug().Model(&user).Where("age = ?", 9999).Update("name", "hello")
}

func MoreColumnUpdate(db *gorm.DB) {

	// todo 注意 当通过 struct 更新时，GORM 只会更新非零字段。 如果您想确保指定字段被更新，你应该使用 Select 更新选定字段，或使用 map 来完成更新操作
	// Updates 方法支持 struct 和 map[string]interface{} 参数。当使用 struct 更新时，默认情况下，GORM 只会更新非零值的字段
	user := insert.User{}
	db.Debug().First(&user)

	fmt.Println(user)

	// 根据 `struct` 更新属性，只会更新非零值的字段
	// UPDATE `users` SET `updated_at`='2022-09-20 15:50:44.021',`name`='hello',`age`=18 WHERE `id` = 1
	db.Debug().Model(&user).Updates(insert.User{Name: "hello", Age: 18})
	// todo 加where 条件，默认都会加上  `deleted_at` IS NULL        加 model  默认都会加上 id=1
	// UPDATE `users` SET `updated_at`='2022-09-20 15:51:59.719',`name`='hello',`age`=188 WHERE age= 18  AND `users`.`deleted_at` IS NULL AND `id` = 1
	db.Debug().Model(&user).Where("age= ? ", 18).Updates(insert.User{Name: "hello", Age: 18})

	// 根据 `map` 更新属性
	// UPDATE `users` SET `age`=18,`name`='hello',`updated_at`='2022-09-20 15:54:56.565' WHERE `id` = 1
	db.Debug().Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18})
	// UPDATE `users` SET `age`=18,`name`='hello',`updated_at`='2022-09-20 15:55:46.891' WHERE age= 18  AND `users`.`deleted_at` IS NULL AND `id` = 1
	db.Debug().Model(&user).Where("age= ? ", 18).Updates(map[string]interface{}{"name": "hello", "age": 18})

	// todo 注意 当通过 struct 更新时，GORM 只会更新非零字段。 如果您想确保指定字段被更新，你应该使用 Select 更新选定字段，或使用 map 来完成更新操作
	// UPDATE `users` SET `updated_at`='2022-09-20 16:00:21.696' WHERE `id` = 1
	db.Debug().Model(&user).Updates(insert.User{Name: "", Age: 0})
	// todo 零值也更新了
	// UPDATE `users` SET `updated_at`='2022-09-20 16:01:22.637',`name`='',`age`=18 WHERE `id` = 1
	db.Debug().Model(&user).Select("name", "age").Updates(insert.User{Name: "", Age: 18})
	//db.Debug().Model(&user).Updates(map[string]interface{}{"name": nil, "age": 0})

	// db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
	// UPDATE users SET age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
	// Select 所有字段（查询包括零值字段的所有字段）
	// db.Model(&user).Select("*").Update(User{Name: "jinzhu", Role: "admin", Age: 0})

	// Select 除 Role 外的所有字段（包括零值字段的所有字段）
	// db.Model(&user).Select("*").Omit("Role").Update(User{Name: "jinzhu", Role: "admin", Age: 0})
}

// BatchUpdate 批量更新
func BatchUpdate(db *gorm.DB) {
	//如果您尚未通过 Model 指定记录的主键，则 GORM 会执行批量更新

	// 根据 struct 更新
	// [rows:17] UPDATE `users` SET `updated_at`='2022-09-20 16:10:40.464',`name`='hello',`age`=18 WHERE age = 18 AND `users`.`deleted_at` IS NULL
	db.Debug().Model(insert.User{}).Where("age = ?", 18).Updates(insert.User{Name: "hello", Age: 18})

	// 根据 map 更新
	// [rows:17] UPDATE `users` SET `age`=18,`name`='hello1' WHERE age = 18
	db.Debug().Table("users").Where("age = ?", 18).Updates(map[string]interface{}{"name": "hello1", "age": 18})

	// 阻止全局更新
	// 如果在没有任何条件的情况下执行批量更新，默认情况下，GORM 不会执行该操作，并返回 ErrMissingWhereClause 错误
	//
	//对此，你必须加一些条件，或者使用原生 SQL，或者启用 AllowGlobalUpdate 模式，

	/**

	db.Model(&User{}).Update("name", "jinzhu").Error // gorm.ErrMissingWhereClause

	db.Model(&User{}).Where("1 = 1").Update("name", "jinzhu")
	// UPDATE users SET `name` = "jinzhu" WHERE 1=1

	db.Exec("UPDATE users SET name = ?", "jinzhu")
	// UPDATE users SET name = "jinzhu"

	db.Session(&gorm.Session{AllowGlobalUpdate: true}).Model(&User{}).Update("name", "jinzhu")
	// UPDATE users SET `name` = "jinzhu"

	*/
}

// ExpressionUpdate sql 表达式更新
func ExpressionUpdate(db *gorm.DB) {

	// 使用 SQL 表达式更新
	//
	//GORM 允许使用 SQL 表达式更新列

	// [rows:1] UPDATE `users` SET `age`= age*2-18,`updated_at`='2022-09-20 16:23:32.554' WHERE  id = 18 AND `users`.`deleted_at` IS NULL
	db.Debug().Model(insert.User{}).Where(" id = ?", 18).Update("age", gorm.Expr(" age*?-?", 2, 18))

	// [rows:1] UPDATE `users` SET `age`= age*2-18,`updated_at`='2022-09-20 16:28:15.85' WHERE  id = 18 AND `users`.`deleted_at` IS NULL
	db.Debug().Model(insert.User{}).Where(" id = ?", 18).Updates(map[string]interface{}{"age": gorm.Expr(" age*?-?", 2, 18)})

	/**
	db.Model(&product).UpdateColumn("quantity", gorm.Expr("quantity - ?", 1))
	// UPDATE "products" SET "quantity" = quantity - 1 WHERE "id" = 3;

	db.Model(&product).Where("quantity > 1").UpdateColumn("quantity", gorm.Expr("quantity - ?", 1))
	// UPDATE "products" SET "quantity" = quantity - 1 WHERE "id" = 3 AND quantity > 1;
	*/

}

// SelectForUpdate 根据子查询更新
func SelectForUpdate(db *gorm.DB) {

	db.Model(insert.User{}).Update("company_name", db.Model(&insert.Person{}).Select("name").Where("companies.id = users.company_id"))
	// UPDATE "users" SET "company_name" = (SELECT name FROM companies WHERE companies.id = users.company_id);

	db.Table("users as u").Where("name = ?", "jinzhu").Update("company_name", db.Table("companies as c").Select("name").Where("c.id = u.company_id"))

	db.Table("users as u").Where("name = ?", "jinzhu").Updates(map[string]interface{}{"company_name": db.Table("companies as c").Select("name").Where("c.id = u.company_id")})
}

func NotTimeUpdate(db *gorm.DB) {
	// 不使用 Hook 和时间追踪
	// 如果您想在更新时跳过 Hook 方法且不追踪更新时间，可以使用 UpdateColumn、UpdateColumns，其用法类似于 Update、Updates
	/**

	// 更新单个列
	db.Model(&user).UpdateColumn("name", "hello")
	// UPDATE users SET name='hello' WHERE id = 111;

	// 更新多个列
	db.Model(&user).UpdateColumns(User{Name: "hello", Age: 18})
	// UPDATE users SET name='hello', age=18 WHERE id = 111;

	// 更新选中的列
	db.Model(&user).Select("name", "age").UpdateColumns(User{Name: "hello", Age: 0})
	// UPDATE users SET name='hello', age=0 WHERE id = 111;

	*/
}

// ReturnUpdate 返回修改行的数据
func ReturnUpdate(db *gorm.DB) {
	// 返回被修改的数据，仅适用于支持 Returning 的数据库，例如：
	var users []insert.User
	db.Debug().Model(&users).Clauses(clause.Returning{}).Where("age = ? ", 18).Updates(insert.User{Name: "hello", Age: 18})

	for user := range users {
		fmt.Println(user)
	}

	/**

	// 返回所有列
	var users []User
	DB.Model(&users).Clauses(clause.Returning{}).Where("role = ?", "admin").Update("salary", gorm.Expr("salary * ?", 2))
	// UPDATE `users` SET `salary`=salary * 2,`updated_at`="2021-10-28 17:37:23.19" WHERE role = "admin" RETURNING *
	// users => []User{{ID: 1, Name: "jinzhu", Role: "admin", Salary: 100}, {ID: 2, Name: "jinzhu.2", Role: "admin", Salary: 1000}}

	// 返回指定的列
	DB.Model(&users).Clauses(clause.Returning{Columns: []clause.Column{{Name: "name"}, {Name: "salary"}}}).Where("role = ?", "admin").Update("salary", gorm.Expr("salary * ?", 2))
	// UPDATE `users` SET `salary`=salary * 2,`updated_at`="2021-10-28 17:37:23.19" WHERE role = "admin" RETURNING `name`, `salary`
	// users => []User{{ID: 0, Name: "jinzhu", Role: "", Salary: 100}, {ID: 0, Name: "jinzhu.2", Role: "", Salary: 1000}}

	*/
}

func ChangeUpdate() {
	/**

	检查字段是否有变更？

	GORM 提供了 Changed 方法，它可以被用在 Before Update Hook 里，它会返回字段是否有变更的布尔值

	Changed 方法只能与 Update、Updates 方法一起使用，并且它只是检查 Model 对象字段的值与 Update、Updates 的值是否相等，如果值有变更，且字段没有被忽略，则返回 true


	func (u *User) BeforeUpdate(tx *gorm.DB) (err error) {
	  // 如果 Role 字段有变更
	    if tx.Statement.Changed("Role") {
	    return errors.New("role not allowed to change")
	    }

	  if tx.Statement.Changed("Name", "Admin") { // 如果 Name 或 Role 字段有变更
	    tx.Statement.SetColumn("Age", 18)
	  }

	  // 如果任意字段有变更
	    if tx.Statement.Changed() {
	        tx.Statement.SetColumn("RefreshedAt", time.Now())
	    }
	    return nil
	}

	db.Model(&User{ID: 1, Name: "jinzhu"}).Updates(map[string]interface{"name": "jinzhu2"})
	// Changed("Name") => true
	db.Model(&User{ID: 1, Name: "jinzhu"}).Updates(map[string]interface{"name": "jinzhu"})
	// Changed("Name") => false, 因为 `Name` 没有变更
	db.Model(&User{ID: 1, Name: "jinzhu"}).Select("Admin").Updates(map[string]interface{
	  "name": "jinzhu2", "admin": false,
	})
	// Changed("Name") => false, 因为 `Name` 没有被 Select 选中并更新

	db.Model(&User{ID: 1, Name: "jinzhu"}).Updates(User{Name: "jinzhu2"})
	// Changed("Name") => true
	db.Model(&User{ID: 1, Name: "jinzhu"}).Updates(User{Name: "jinzhu"})
	// Changed("Name") => false, 因为 `Name` 没有变更
	db.Model(&User{ID: 1, Name: "jinzhu"}).Select("Admin").Updates(User{Name: "jinzhu2"})
	// Changed("Name") => false, 因为 `Name` 没有被 Select 选中并更新

	*/
}
